package com.itlong.cloud.face.baidu;

import com.alibaba.fastjson.JSON;
import com.baidu.aip.face.AipFace;
import com.itlong.cloud.POJO.PO.AppUserProjectPO;
import com.itlong.cloud.POJO.PO.DeviceBusinessChangeLogPO;
import com.itlong.cloud.POJO.PO.DeviceBusinessEntranceChangeLogPO;
import com.itlong.cloud.POJO.VO.app.AppCreateBaiduFaceVO;
import com.itlong.cloud.PlatformConstants;
import com.itlong.cloud.commons.dao.IAppUserProjectDao;
import com.itlong.cloud.constants.RabbitMQConstant;
import com.itlong.cloud.enumerate.CloudEntranceChangeBusinessEnum;
import com.itlong.cloud.enumerate.SmartCommunityEnum;
import com.itlong.cloud.enumerate.ecode.AppErrorCodeEnum;
import com.itlong.cloud.enumerate.ecode.BaseErrorCodeEnum;
import com.itlong.cloud.push.JPushClientService;
import com.itlong.cloud.rabbitmq.config.MessageSender;
import com.itlong.cloud.utils.node.JsonUtil;
import com.itlong.cloud.utils.picture.VerifyCodeUtils;
import org.apache.commons.lang.StringUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Service
@RefreshScope
public class BaiduFaceService {
    private static final Logger LOG = LoggerFactory.getLogger(BaiduFaceService.class);

    @Value("${" + PlatformConstants.BAIDU_FACE_GROUPID + "}")
    public String BAIDU_FACE_GROUPID;

    @Value("${" + PlatformConstants.PROPERTY_OPERATE_MINI_CONTENT_AES_ENCRYPT_KEY + "}")
    String propertyOperateMiniContentAesEncryptKey;//物业,运营,小程序

    @Autowired
    BaiduFaceCreateQueue baiduFaceCreateQueue;

    @Autowired
    IAppUserProjectDao iAppUserProjectDao;

    @Autowired
    JPushClientService jPushClientService;

    private ExecutorService cachedThreadPool ; //线程池

    @Autowired
    private MessageSender messageSender;

    /**
     * <desc>
     *     人脸注册(将请求放入队列)
     * </desc>
     *
     * @param appCreateBaiduFaceVO
     * @return
     * @author Qiang.S
     * @createDate 2018/04/23
     */
    public boolean createBaiduFace(AppCreateBaiduFaceVO appCreateBaiduFaceVO){
        try{
            baiduFaceCreateQueue.getBaiduFaceCreateQueue().put(appCreateBaiduFaceVO);
            if (!(StringUtils.isBlank(appCreateBaiduFaceVO.getUserInfo()) || appCreateBaiduFaceVO.getUid().startsWith("V_"))) {
                List<DeviceBusinessChangeLogPO> changeLogPOS = new ArrayList<>();
                com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject();
                json.put("userId", appCreateBaiduFaceVO.getUid());
                json.put("projectId", appCreateBaiduFaceVO.getProjectId());
                DeviceBusinessChangeLogPO deviceBusinessChangeLogPO =
                        new DeviceBusinessChangeLogPO(SmartCommunityEnum.PUSH_USER_PROJECT, json,appCreateBaiduFaceVO.getProjectId());
                changeLogPOS.add(deviceBusinessChangeLogPO);
                com.alibaba.fastjson.JSONObject dataJs = new com.alibaba.fastjson.JSONObject();
                dataJs.put("data",changeLogPOS);
                //发送云对讲指令
                messageSender.send(RabbitMQConstant.RABBITMQ_HANDLE_SMART_COMMUNITY_ORDERS_QUEUE,
                        JsonUtil.toJSON(dataJs), RabbitMQConstant.RABBITMQ_SMART_COMMUNITY_ORDERS_EXCHANGE,
                        RabbitMQConstant.RABBITMQ_SMART_COMMUNITY_ORDERS_ROUTING_KEY);
                // 云门禁
                List<DeviceBusinessEntranceChangeLogPO> changeEntranceLogPOS = new ArrayList<>();
                changeEntranceLogPOS.add(new DeviceBusinessEntranceChangeLogPO(CloudEntranceChangeBusinessEnum.PUSH_USER_PROJECT, json, appCreateBaiduFaceVO.getProjectId()));
                dataJs.put("data", changeEntranceLogPOS);
                //发送云门禁指令
                messageSender.send(
                        RabbitMQConstant.RABBITMQ_HANDLE_CLOUD_ENTRANCE_CHANGE_BUSINESS_QUEUE,
                        JSON.toJSONString(dataJs),
                        RabbitMQConstant.RABBITMQ_CLOUD_ENTRANCE_CHANGE_BUSINESS_EXCHANGE,
                        RabbitMQConstant.RABBITMQ_CLOUD_ENTRANCE_CHANGE_BUSINESS_ROUTING_KEY
                );
            }
        }catch (Exception e){
            LOG.error("人脸注册失败加入队列失败！e:" +  e.getMessage());
            return  false;
        }
        return  true;
    }

    /**
     * <desc>
     *     活体检测接口
     * </desc>
     *
     * @param appCreateBaiduFaceVO
     * @return
     * @author Qiang.S
     * @createDate 2018/05/21
     */
    public String faceVerify(AppCreateBaiduFaceVO appCreateBaiduFaceVO) {
        AipFace client;
        JSONObject res = null;
        try {
            //百度推荐client使用单例,但是有多个项目不同id,所以这里每次重新创建百度client
            client = new AipFace(appCreateBaiduFaceVO.getAPP_ID(), appCreateBaiduFaceVO.getAPI_KEY(), appCreateBaiduFaceVO.getSECRET_KEY());
            // 传入可选参数调用接口
            HashMap<String, String> options = new HashMap<String, String>();
            options.put("max_face_num", "10");
            options.put("face_fields", "landmark,qualities,faceliveness");
            //防止qps过量,重试5次间隔0.5秒
            int i = 0;
            Object error_code = null;
            Object error_msg = null;
            //2019-06-03服务间传递图片从数据改为传递地址
            if (!StringUtils.isBlank(appCreateBaiduFaceVO.getImageUrl())) {
                LOG.info("【faceVerify】图片解析开始");
                long startTime = System.currentTimeMillis();
                appCreateBaiduFaceVO.setFile(VerifyCodeUtils.getFileStream(appCreateBaiduFaceVO.getImageUrl(),propertyOperateMiniContentAesEncryptKey));
                long endTime = System.currentTimeMillis();
                LOG.info("【faceVerify】图片解析结束" + (endTime - startTime));
            }
            while (i < 5) {
                LOG.info("【faceVerify】人脸认证开始");
                long startTime = System.currentTimeMillis();
                res = client.faceverify(appCreateBaiduFaceVO.getFile(), options);
                long endTime = System.currentTimeMillis();
                LOG.info("【faceVerify】人脸认证结束" + (endTime - startTime));
                Map<String, Object> map = JsonUtil.JsonToMap(res.toString());
                error_code = map.get("error_code");
                error_msg = map.get("error_msg");
                //error_code为18,qps超量异常准备重试,否则跳出
                if (error_code == null || !"18".equals(error_code.toString())) {
                    break;
                }
                i++;
                Thread.sleep(500);//等待0.5秒
            }
            LOG.info("活体验证百度人脸返回:" + res.toString());
            if (error_code != null || error_msg != null) {
                if ("18".equals(error_code.toString())) {
                    return "busy";
                }
                return "error";
            }
            int result_num = Integer.parseInt(res.get("result_num").toString());//人脸数
            JSONArray result = res.getJSONArray("result");
            if (result_num == 0) {//无人脸
                return "others";
            }
            JSONObject myjObject = result.getJSONObject(0);
            float faceliveness = Float.parseFloat(myjObject.get("faceliveness").toString());
            float pitch = Float.parseFloat(myjObject.get("pitch").toString());//俯仰角度
            float roll = Float.parseFloat(myjObject.get("roll").toString());//平面角度
            float yaw = Float.parseFloat(myjObject.get("yaw").toString());//三维角度
            Object data = myjObject.get("qualities");
            JSONObject jsonObject = new JSONObject(data.toString());
            float blur = Float.parseFloat(jsonObject.get("blur").toString());//模糊度
            float illumination = Float.parseFloat(jsonObject.get("illumination").toString());//光照范围
            float completeness = Float.parseFloat(jsonObject.get("completeness").toString());//完整度
            data = jsonObject.get("occlusion");
            JSONObject occlusion = new JSONObject(data.toString());
            float left_eye = Float.parseFloat(occlusion.get("left_eye").toString());//左眼
            float right_eye = Float.parseFloat(occlusion.get("right_eye").toString());//右眼
            float nose = Float.parseFloat(occlusion.get("nose").toString());//鼻子
            float mouth = Float.parseFloat(occlusion.get("mouth").toString());//嘴
            float left_cheek = Float.parseFloat(occlusion.get("left_cheek").toString());//左脸颊
            float right_cheek = Float.parseFloat(occlusion.get("right_cheek").toString());//右脸颊
            float chin = Float.parseFloat(occlusion.get("chin").toString());//下巴
            JSONObject location = new JSONObject(myjObject.get("location").toString());
            float width = Float.parseFloat(location.get("width").toString());
            float height = Float.parseFloat(location.get("height").toString());
            JSONArray landmark = myjObject.getJSONArray("landmark");
            JSONObject landmark1 = new JSONObject(landmark.get(0).toString());
            JSONObject landmark2 = new JSONObject(landmark.get(1).toString());
            float x1 = Float.parseFloat(landmark1.get("x").toString());
            float x2 = Float.parseFloat(landmark2.get("x").toString());
            // 人脸个数判断
            if (result_num > 1) {
                return "result_num";
            }
            //头像大小判断
            LOG.info("width:" + width+" | height:"+height);
            if (width < 100 || height < 100) {
                return "size";
            }
            //注销瞳距相关判断
            if (x2-x1 < 90) {
                return "size";
            }
            //人脸完整度判断
            if (completeness != 1) {
                return "completeness";
            }
            //遮挡范围判断
            if (left_eye > 0.3) {
                return "left_eye";
            }
            if (right_eye > 0.3) {
                return "right_eye";
            }
            if (nose > 0.3) {
                return "nose";
            }
            if (mouth > 0.3) {
                return "mouth";
            }
            if (left_cheek > 0.3) {
                return "left_cheek";
            }
            if (right_cheek > 0.3) {
                return "right_cheek";
            }
            if (chin > 0.3) {
                return "chin";
            }
            //活体值判断,推荐值为0.393241
            if (faceliveness < 0.39) {
                return "faceliveness";
            }
            //模糊度判断
            if (blur > 0.1) {
                return "blur";
            }
            //光照范围判断
            if (illumination < 90) {
                return "illumination_min";
            }
            //光照范围判断
            if (illumination > 200) {
                return "illumination_max";
            }
            //姿态角度判断
            if (pitch > 20 || pitch <-20) {
                return "pitch";
            }
            if (roll > 20 || roll <-20) {
                return "roll";
            }
            if (yaw > 20 || yaw < -20) {
                return "yaw";
            }
        } catch (Exception e) {
            LOG.error("活体检测接口调用失败！e:" + e.getMessage());
        }
        return null;
    }

    /**
     * <desc>
     *     人脸对比接口
     * </desc>
     *
     * @param appCreateBaiduFaceVO
     * @return
     * @author Qiang.S
     * @createDate 2018/06/07
     */
    public String identifyUser(AppCreateBaiduFaceVO appCreateBaiduFaceVO){
        AipFace client;
        JSONObject res = null;
        try{
            //百度推荐client使用单例,但是有多个项目不同id,所以这里每次重新创建百度client
            client = new AipFace(appCreateBaiduFaceVO.getAPP_ID(), appCreateBaiduFaceVO.getAPI_KEY(), appCreateBaiduFaceVO.getSECRET_KEY());
            // 传入可选参数调用接口
            HashMap<String, String> options = new HashMap<String, String>();
            options.put("ext_fields", "");
            options.put("user_top_num", "1");//返回top数
            //防止qps过量,重试5次间隔0.5秒
            int i = 0;
            Object error_code = null;
            Object error_msg = null;
            //2019-06-03服务间传递图片从数据改为传递地址
            if (!StringUtils.isBlank(appCreateBaiduFaceVO.getImageUrl())) {
                LOG.info("【BaiduFaceService】解析图片地址开始");
                long startTime = System.currentTimeMillis();
                appCreateBaiduFaceVO.setFile(VerifyCodeUtils.getFileStream(appCreateBaiduFaceVO.getImageUrl(),propertyOperateMiniContentAesEncryptKey));
                long endTime = System.currentTimeMillis();
                LOG.info("【BaiduFaceService】解析图片地址结束" + (endTime - startTime));
            }
            while (i < 5) {
                LOG.info("【BaiduFaceService】百度认证用户图片开始");
                long startTime = System.currentTimeMillis();
                res = client.identifyUser(BAIDU_FACE_GROUPID,appCreateBaiduFaceVO.getFile(), options);
                long endTime = System.currentTimeMillis();
                LOG.info("【BaiduFaceService】百度认证用户图片结束" + (endTime - startTime));
                Map<String, Object> map = JsonUtil.JsonToMap(res.toString());
                error_code = map.get("error_code");
                error_msg = map.get("error_msg");
                //error_code为18,qps超量异常准备重试,否则跳出
                if (error_code == null || !"18".equals(error_code.toString())) {
                    break;
                }
                i++;
                Thread.sleep(500);//等待0.5秒
            }
            LOG.info("人脸对比百度人脸返回:"+res.toString());
            if (error_code != null || error_msg != null) {
                if ("18".equals(error_code.toString())) {
                    return "busy";
                }
                //人脸库中无此照片
                if ("216618".equals(error_code.toString())) {
                    return null;
                }
                //人脸库中无此照片
                if ("216402".equals(error_code.toString())) {
                    return null;
                }
                return "error";
            }
            JSONArray result = res.getJSONArray("result");
            JSONObject myjObject = result.getJSONObject(0);
            String scores = myjObject.get("scores").toString();//获取最高分
            String uid = myjObject.get("uid").toString();
            if (scores != null && !"".equals(scores)) {
                scores = scores.replace("[","").replace("]","");
            }
            float scoresFloat =  Float.parseFloat(scores);//对比分数
            // 判断是否有通过人脸在库中
            if (scoresFloat > appCreateBaiduFaceVO.getFaceScore()) {
                if (uid !=null && appCreateBaiduFaceVO.getUid() != null && !uid.equals(appCreateBaiduFaceVO.getUid())) {
                    return "face_repeat";
                }else {//访客人脸使用
                    return uid;
                }
            }
        }catch (Exception e){
            LOG.error("人脸对比接口调用失败！e:" +  e.getMessage());
        }
        return  null;
    }

    /**
     * <desc>
     *     人脸删除接口
     * </desc>
     *
     * @param appCreateBaiduFaceVO
     * @return
     * @author Qiang.S
     * @createDate 2019/03/01
     */
    public String deleteUser(AppCreateBaiduFaceVO appCreateBaiduFaceVO){
        AipFace client;
        JSONObject res = null;
        try{
            //百度推荐client使用单例,但是有多个项目不同id,所以这里每次重新创建百度client
            client = new AipFace(appCreateBaiduFaceVO.getAPP_ID(), appCreateBaiduFaceVO.getAPI_KEY(), appCreateBaiduFaceVO.getSECRET_KEY());
            // 传入可选参数调用接口
            HashMap<String, String> options = new HashMap<String, String>();
            options.put("group_id", BAIDU_FACE_GROUPID);
            //防止qps过量,重试5次间隔0.5秒
            int i = 0;
            Object error_code = null;
            Object error_msg = null;
            while (i < 5) {
                res = client.deleteUser(appCreateBaiduFaceVO.getUid(), options);
                Map<String, Object> map = JsonUtil.JsonToMap(res.toString());
                error_code = map.get("error_code");
                error_msg = map.get("error_msg");
                //error_code为18,qps超量异常准备重试,否则跳出
                if (error_code == null || !"18".equals(error_code.toString())) {
                    break;
                }
                i++;
                Thread.sleep(500);//等待0.5秒
            }
            LOG.info("人脸删除百度人脸返回:"+res.toString());
            if (error_code != null || error_msg != null) {
                if ("18".equals(error_code.toString())) {
                    return "busy";
                }
                return "error";
            }

        }catch (Exception e){
            LOG.error("人脸删除调用失败！e:" +  e.getMessage());
        }
        return  null;
    }

    /**
     * <desc>
     *     百度人脸注册
     * </desc>
     *
     * @return
     * @author Qiang.S
     * @createDate 2018/04/23
     */
    public void doCreateBaiduFace(){
        AipFace client;
        JSONObject res = null;
        int i = 0;
        Object error_code = null;
        Object error_msg = null;
        Object log_id;
        while (true) {
            try {
                AppCreateBaiduFaceVO appCreateBaiduFaceVO = baiduFaceCreateQueue.getBaiduFaceCreateQueue().take();
                if (appCreateBaiduFaceVO != null) {
                    //百度推荐client使用单例,但是有多个项目不同id,所以这里每次重新创建百度client
                    client = new AipFace(appCreateBaiduFaceVO.getAPP_ID(), appCreateBaiduFaceVO.getAPI_KEY(), appCreateBaiduFaceVO.getSECRET_KEY());
                    HashMap<String, String> options = new HashMap<String, String>();
                    options.put("action_type", "replace");//无则新增,有则更新
                    //2019-06-03服务间传递图片从数据改为传递地址
                    if (!StringUtils.isBlank(appCreateBaiduFaceVO.getImageUrl())) {
                        appCreateBaiduFaceVO.setFile(VerifyCodeUtils.getFileStream(appCreateBaiduFaceVO.getImageUrl(),propertyOperateMiniContentAesEncryptKey));
                    }
                    //防止qps过量,重试5次间隔0.5秒
                    while (i < 5) {
                        res = client.addUser(appCreateBaiduFaceVO.getUid(), appCreateBaiduFaceVO.getUserInfo(), BAIDU_FACE_GROUPID, appCreateBaiduFaceVO.getFile(), options);
                        Map<String, Object> map = JsonUtil.JsonToMap(res.toString());
                        error_code = map.get("error_code");
                        error_msg = map.get("error_msg");
                        log_id = map.get("log_id");
                        //error_code为18,qps超量异常准备重试,否则跳出
                        if (error_code == null || !"18".equals(error_code.toString())) {
                            break;
                        }
                        i++;
                        Thread.sleep(500);//等待0.5秒
                    }
                    i = 0;//重置
                    LOG.info("创建百度人脸返回:"+res.toString());
                    Map<String,String> comands = new HashMap<String,String>();

                    //创建百度人脸为异步处理,在百度创建成功后进行数据固化,数据固化成功后进行极光推送
                    AppUserProjectPO appUserProjectPO = new AppUserProjectPO();
                    appUserProjectPO.setUserId(appCreateBaiduFaceVO.getUid());
                    appUserProjectPO.setProjectId(appCreateBaiduFaceVO.getProjectId());
                    appUserProjectPO.setFacePicUrl(appCreateBaiduFaceVO.getUploadFilePath());
                    appUserProjectPO.setFaceCreateTime(new Date());
                    appUserProjectPO.setUpdateTime(new Date());
                    int isFace = 3;//失败
                    if (error_code == null && error_msg == null) {
                        isFace = 1;//成功
                    }
                    appUserProjectPO.setIsFace(isFace);
                    int updateNumber = iAppUserProjectDao.update(appUserProjectPO);
                    if (appCreateBaiduFaceVO.getJPushFlag() != null && "1".equals(appCreateBaiduFaceVO.getJPushFlag())) {
                        if (updateNumber > 0) {
                            //强推送到app端,通知用户注册完成
                            comands.put("isFace",Integer.toString(isFace));
                            boolean result = jPushClientService.pushWithFaceVerificationTask(appCreateBaiduFaceVO.getUserPhone(), BaseErrorCodeEnum.SUCCESS.getErrorCode().getCodeMsg() ,comands);
                            if (!result) {
                                LOG.info("【APP_人脸注册_注册成功_极光推送消息失败】");
                            }
                            continue;
                        }
                        comands.put("isFace","0");//未更新成功返回 未认证状态
                        boolean result = jPushClientService.pushWithFaceVerificationTask(appCreateBaiduFaceVO.getUserPhone(), AppErrorCodeEnum.APP_FACE_CREATE_FAIL.getErrorCode().getCodeMsg(),comands);
                        if (!result) {
                            LOG.info("【APP_人脸注册_注册失败_极光推送消息失败】");
                        }
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
                LOG.error("人脸注册失败！e:" + e.getMessage());
            } catch (Exception e) {
                e.printStackTrace();
                LOG.error("【APP_人脸注册_极光推送或更新数据库异常】");
            }
        }
    }

    /**
     * <desc>
     *     百度访客人脸注册
     * </desc>
     *
     * @param appCreateBaiduFaceVO 人脸注册信息
     * @return
     * @author Qiang.S
     * @createDate 2019/03/06
     */
    public boolean doCreateBaiduFaceForVisitor(AppCreateBaiduFaceVO appCreateBaiduFaceVO){
        AipFace client;
        JSONObject res = null;
        int i = 0;
        Object error_code = null;
        Object error_msg = null;
            try {
                if (appCreateBaiduFaceVO != null) {
                    //百度推荐client使用单例,但是有多个项目不同id,所以这里每次重新创建百度client
                    client = new AipFace(appCreateBaiduFaceVO.getAPP_ID(), appCreateBaiduFaceVO.getAPI_KEY(), appCreateBaiduFaceVO.getSECRET_KEY());
                    HashMap<String, String> options = new HashMap<String, String>();
                    options.put("action_type", "replace");//无则新增,有则更新
                    //2019-06-03服务间传递图片从数据改为传递地址
                    if (!StringUtils.isBlank(appCreateBaiduFaceVO.getImageUrl())) {
                        appCreateBaiduFaceVO.setFile(VerifyCodeUtils.getFileStream(appCreateBaiduFaceVO.getImageUrl(),propertyOperateMiniContentAesEncryptKey));
                    }
                    //防止qps过量,重试5次间隔0.5秒
                    while (i < 5) {
                        res = client.addUser(appCreateBaiduFaceVO.getUid(), appCreateBaiduFaceVO.getUserInfo(), BAIDU_FACE_GROUPID, appCreateBaiduFaceVO.getFile(), options);
                        Map<String, Object> map = JsonUtil.JsonToMap(res.toString());
                        error_code = map.get("error_code");
                        error_msg = map.get("error_msg");
                        //error_code为18,qps超量异常准备重试,否则跳出
                        if (error_code == null || !"18".equals(error_code.toString())) {
                            break;
                        }
                        i++;
                        Thread.sleep(500);//等待0.5秒
                    }
                    i = 0;//重置
                    LOG.info("创建百度人脸返回:"+res.toString());
                    boolean isFace = false;//失败
                    if (error_code == null && error_msg == null) {
                        isFace = true;//成功
                    }
                    return isFace;
                }
            } catch (Exception e) {
                e.printStackTrace();
                LOG.error("【人脸注册_访客人脸注册异常】");
            }
        return false;
    }

    /**
     * <desc>
     *      初始化
     * </desc>
     *
     * @author Qiang.S
     * @createDate 2018/04/23
     */
    @PostConstruct
    public void init(){
        int max = 5;
        cachedThreadPool = Executors.newFixedThreadPool(max);
        for (int i = 0; i < max; i++) {
            cachedThreadPool.execute(() -> {
                doCreateBaiduFace();
            });
        }
    }
}
